package utils

import (
	"crypto/hmac"
	"crypto/md5"
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	mathRand "math/rand"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/fatih/color"
	"github.com/rs/xid"
)

// PrintMsg 输出默认字体
func PrintMsg(msg ...interface{}) {
	var m []interface{}
	for _, s := range msg {
		m = append(m, s, "\t")
	}
	m = m[:len(m)-1]
	fmt.Println(m...)
}

// PrintErrMsg 输出红色字体
func PrintErrMsg(msg ...interface{}) {
	var m []interface{}
	for _, s := range msg {
		m = append(m, "\t", s)
	}
	c := color.New(color.FgRed)
	_, _ = c.Println(m...)
}

// PrintOkMsg 输出绿色字体
func PrintOkMsg(msg ...interface{}) {
	var m []interface{}
	for _, s := range msg {
		m = append(m, "\t", s)
	}
	c := color.New(color.FgGreen)
	_, _ = c.Println(m...)
}

// Try 若第一个方法返回错误，则依次执行其后面的方法，若后面的方法中有一个报错，则立即从头再来，直到第一个方法不报错，或者达到times次数
func Try(times int, fns ...Fn) (err error) {
	for ; times > 0; times-- {
		l := len(fns)
		if l == 0 {
			return
		}

		if err = fns[0](); err == nil || times == 1 {
			return
		}

		for i := 1; i < l; i++ {
			if err = fns[i](); err != nil {
				break
			}
		}
	}
	return
}

func CreateErr(strs ...interface{}) error {
	res := make([]string, 0)
	for _, str := range strs {
		res = append(res, Str(str))
	}
	return fmt.Errorf(strings.Join(res, Str("：")))
}

func Join(s interface{}, strs ...interface{}) string {
	res := make([]string, 0)
	for _, str := range strs {
		res = append(res, Str(str))
	}
	return strings.Join(res, Str(s))
}

func Echo(msg ...interface{}) {
	msgs := make([]string, 0)
	for _, m := range msg {
		msgs = append(msgs, Str(m))
	}
	fmt.Println(strings.Join(msgs, "\t"))
}

func JSON(data interface{}) (j string, e error) {
	b, e := json.MarshalIndent(data, "", "    ")
	if e != nil {
		return
	}
	j = string(b)
	return
}

func PrintJSON(data interface{}) (j string) {
	j, _ = JSON(data)
	fmt.Println(j)
	return
}

func IifStr(condition bool, whenTrue, whenFalse string) string {
	if condition {
		return whenTrue
	} else {
		return whenFalse
	}
}

func IifInt(condition bool, whenTrue, whenFalse int) int {
	if condition {
		return whenTrue
	} else {
		return whenFalse
	}
}

func IifInt64(condition bool, whenTrue, whenFalse int64) int64 {
	if condition {
		return whenTrue
	} else {
		return whenFalse
	}
}

func IifFloat32(condition bool, whenTrue, whenFalse float32) float32 {
	if condition {
		return whenTrue
	} else {
		return whenFalse
	}
}

func IifFloat64(condition bool, whenTrue, whenFalse float64) float64 {
	if condition {
		return whenTrue
	} else {
		return whenFalse
	}
}

// Nvl 若str为空，则返回toReplace中第一个不为""的值
func Nvl(str string, toReplace ...string) string {
	if str == "" {
		for _, s := range toReplace {
			if s != "" {
				return s
			}
		}
	}
	return str
}

// Nvi 若num<=0，则返回toReplace中第一个不为>0的值
func Nvi(num int, toReplace ...int) int {
	if num <= 0 {
		for _, s := range toReplace {
			if s > 0 {
				return s
			}
		}
	}
	return num
}

// Nvi64 若num<=0，则返回toReplace中第一个不为>0的值
func Nvi64(num int64, toReplace ...int64) int64 {
	if num <= 0 {
		for _, s := range toReplace {
			if s > 0 {
				return s
			}
		}
	}
	return num
}

// Nvf 若num<=0，则返回toReplace中第一个不为>0的值
func Nvf(num float32, toReplace ...float32) float32 {
	if num <= 0 {
		for _, s := range toReplace {
			if s > 0 {
				return s
			}
		}
	}
	return num
}

// Nvf64 若num<=0，则返回toReplace中第一个不为>0的值
func Nvf64(num float64, toReplace ...float64) float64 {
	if num <= 0 {
		for _, s := range toReplace {
			if s > 0 {
				return s
			}
		}
	}
	return num
}

func Trim(str string, cutset ...string) string {
	if len(cutset) == 0 {
		cutset = []string{"(^[\\s\\t\\r\\n]+|[\\s\\t\\r\\n]+$)"}
	}

	for i, s := range cutset {
		r, err := regexp.Compile(s)
		if err != nil {
			PrintErrMsg("utils.Trim()", fmt.Sprintf(`第【%v】个正则【%v】有误`, i, s))
			panic(err)
		}
		str = r.ReplaceAllString(str, "")
	}
	return str
}

func RandInt(min, max int) int {
	time.Sleep(time.Nanosecond * 1)
	mathRand.Seed(time.Now().UnixNano())
	return min + mathRand.Intn(max-min)
}

func RandNum(length int) (str string) {
	for i := 0; i < length; i++ {
		str += Str(RandInt(0, 9))
	}
	return
}

//Md5 []byte转md5
func Md5(b []byte) string {
	return fmt.Sprintf("%x", md5.Sum(b))
}

//Md5Str string转md5
func Md5Str(str string) string {
	return Md5([]byte(str))
}

func Sha256(b []byte) string {
	var h = sha256.New()
	h.Write(b)
	return fmt.Sprintf("%x", h.Sum(nil))
}

func Sha256Str(str string) string {
	return Sha256([]byte(str))
}

func HmacSha256(secret, data []byte) string {
	h := hmac.New(sha256.New, secret)
	h.Write(data)
	return hex.EncodeToString(h.Sum(nil))
}

func HmacSha256Str(secret, data string) string {
	return HmacSha256([]byte(secret), []byte(data))
}

// RandLocation 生成国内随机经纬度
func RandLocation() (lat float64, lon float64) {
	var (
		minLat = 3860000
		maxLat = 53550000
		minLon = 73660000
		maxLon = 135050000
	)

	return float64(RandInt(minLat, maxLat)) / 1000000, float64(RandInt(minLon, maxLon)) / 1000000
}

func RandStr(num int) string {
	id := Str(RandInt(10, 99)) + XID() + Str(RandInt(1000000000, 9999999999))
	l := len(id)
	return id[0:IifInt(num <= 0, 1, IifInt(num > l, l, num))]
}

func RandKey(num int) string {
	var str string
	for i := 0; i < num; i += 32 {
		str += Md5([]byte(xid.New().String()))
	}
	return str[0:num]
}

// RandIMEI 生成随机的手机IMEI串号
func RandIMEI() string {
	str := Str(1000000+RandInt(0, 8999999)) + Str(1000000+RandInt(0, 8999999))
	arr := strings.Split(str, "")

	a, b := 0, 0
	for i, l := 0, len(arr); i < l; i++ {
		m, _ := strconv.Atoi(arr[i])
		if i%2 == 0 {
			a += m
		} else {
			n := m * 2
			b += n/10 + n%10
		}
	}

	r := (a + b) % 10
	if r != 0 {
		r = 10 - r
	}

	return str + Str(r)
}

func XID() string {
	return Md5([]byte(xid.New().String()))
}

//UniqID 生成唯一字串
func UniqID() string {
	b := make([]byte, 48)
	if _, err := io.ReadFull(rand.Reader, b); err != nil {
		return ""
	}
	return Md5([]byte(base64.URLEncoding.EncodeToString(b)))
}

//Str 转为string
func Str(v interface{}) string {
	str := fmt.Sprintf("%v", v)
	return IifStr(str == "<nil>", "", str)
}

func Space(n int) string {
	var arr []string
	for ; n > 0; n-- {
		arr = append(arr, "")
	}
	return strings.Join(arr, " ")
}
